package spade;

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;



class MessageReceiver extends Thread
{
	public final ReentrantLock	      post_mutex	= new ReentrantLock();
	public final ReentrantLock	      mutex	       = new ReentrantLock();
	public final Condition	          not_empty	   = mutex.newCondition();
	// private final Condition not_full = mutex.newCondition();
	public BlockingQueue<ACLMessage>	__messages	=
	                                                       new LinkedBlockingQueue<ACLMessage>();

	private ACLMessage __getMessage(boolean block, double tout)
	{
		ACLMessage message;
		try
		{
			if (block)
				message = __messages.poll((long)(tout*1000), TimeUnit.MILLISECONDS);
			else
				message = __messages.poll();
		}
		catch (Exception e)
		{
			message = null;
		}

		return message;
	}
	public ACLMessage _receive(boolean block, double timeout,
	        MessageTemplate template)
	{
		/**
		 * waits for a message during timeout if timeout == None waits until a
		 * message is received if no message is received returns None
		 */
		if (template == null)
		{
			return __getMessage(block, timeout);
		}
		else
		{
			mutex.lock();
			Iterator<ACLMessage> it = __messages.iterator();
			Vector<ACLMessage> v = new Vector<ACLMessage>();
			ACLMessage result = null;
			while (it.hasNext())
			{
				ACLMessage msg = it.next();
				if (template.match(msg))
				{
					// if (msg.match(template)){
					if (result == null)
						result = msg;
					else
						v.add(msg);
				}
				else
				{
					v.add(msg);
				}
			}

			__messages = new LinkedBlockingQueue<ACLMessage>(v);
			mutex.unlock();
			if (result != null)
				return result;

			if (block == false)
				return null;
			else
			{
				double endtime;
				double remaining;
				endtime = (double)(System.currentTimeMillis() / 1000) + timeout;
				ACLMessage result2 = null;
				while (true)
				{
					mutex.lock();
					Iterator<ACLMessage> it2 = __messages.iterator();
					Vector<ACLMessage> v2 = new Vector<ACLMessage>();
					while (it2.hasNext())
					{
						ACLMessage msg = it2.next();
						if (template.match(msg))
						{
							if (result2 == null)
								result2 = msg;
							else
								v2.add(msg);
						}
						else
						{
							v2.add(msg);
						}
					}
					__messages = new LinkedBlockingQueue<ACLMessage>(v2);
					if (result2 != null)
					{
						mutex.unlock();
						return result2;
					}
					remaining = endtime - (System.currentTimeMillis() / 1000);
					if (timeout <= 0.0 && remaining <= 0.0)
					{
						mutex.unlock();
						return null;
					}
					try
					{
						not_empty.await((long)(remaining*1000), TimeUnit.MILLISECONDS);
					}
					catch (InterruptedException e)
					{
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
	}

	boolean postMessage(ACLMessage message)
	{
		if (message != null)
		{
			post_mutex.lock();
			mutex.lock();
			try
			{
				__messages.put(message);
			}
			catch (InterruptedException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			not_empty.notify();
			mutex.unlock();
			post_mutex.unlock();
		}
		return true;
	}
}
